package mygame;

import character.ai.AIAppState;
import character.AbstractCharacter;
import character.CharacterCreator;
import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.BulletAppState.ThreadingType;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.niftygui.NiftyJmeDisplay;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Node;
import com.jme3.system.AppSettings;
import gameobject.construct.Construct;
import gameobject.construct.Tree;
import gameobject.item.Axe;
import gameobject.item.Hammer;
import gameobject.item.Shovel;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Main extends SimpleApplication {
    protected static Main app;

    public static void main(String[] args) {
        app = new Main();
	app.preStart();
        app.start();
    }

    protected void preStart() {
        Logger.getLogger("").setLevel(Level.INFO);
	AppSettings cfg = new AppSettings(true);
	cfg.setFrameRate(60); // set to less than or equal screen refresh rate
	//cfg.setVSync(true);   // prevents page tearing - destroying my framerate?
	//cfg.setFrequency(60); // set to screen refresh rate
	cfg.setResolution(800, 600);
	cfg.setFullscreen(false);
	//cfg.setSamples(2);    // anti-aliasing
	cfg.setTitle("My Game"); // branding: window name
	//try {
	  // Branding: window icon
	  //cfg.setIcons(new BufferedImage[]{ImageIO.read(new File("assets/Interface/icon.gif"))});
	//} catch (IOException ex) {
	  //Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Icon missing.", ex);
	//}
	// branding: load splashscreen from assets
	cfg.setSettingsDialogImage("Interface/MySplashscreen.png");
	setShowSettings(false);
	setSettings(cfg);
    }

    @Override
    public void simpleInitApp() {
        initDataStore();
        StateSwitcher.startNewGame();
    }

    protected void initDataStore() {
	DataStore.stateManager = stateManager;
	DataStore.assetManager = assetManager;
        DataStore.inputManager = inputManager;
	DataStore.viewPort = viewPort;

        initMultithreading();
	initAppStates();
	initHubNodes();
	initPlayer();
	initGui();
    }
    
    protected void initMultithreading() {
        DataStore.app = app;
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(0);
        DataStore.executor = executor;
    }

    protected void initAppStates() {
	BulletAppState bulletAppState = new BulletAppState();
	bulletAppState.setThreadingType(ThreadingType.PARALLEL);
        DataStore.bulletAppState = bulletAppState;

	AIAppState aiAppState = new AIAppState();
        DataStore.aiAppState = aiAppState;
        
        InGameInputAppState inGameInput = new InGameInputAppState();
        DataStore.inGameInputAppState = inGameInput;
    }

    protected void initHubNodes() {
	DataStore.rootNode = rootNode;

	WorldStateNode worldStateNode = new WorldStateNode(Constants.WORLD_STATE_NODE);
	Node naturalHubNode = new Node(Constants.HUB_NODE_UNSELECTABLE_CONSTRUCT);
	worldStateNode.setHubNodeUnselectableConstruct(naturalHubNode);
	Node selectableHubNode = new Node(Constants.HUB_NODE_SELECTABLE);
	worldStateNode.setHubNodeSelectable(selectableHubNode);
	Node interactiveHubNode = new Node(Constants.HUB_NODE_SELECTABLE_CONSTRUCT);
	worldStateNode.setHubNodeSelectableConstruct(interactiveHubNode);
	Node characterHubNode = new Node(Constants.HUB_NODE_CHARACTER);
	worldStateNode.setHubNodeCharacter(characterHubNode);
        DataStore.worldStateNode = worldStateNode;

	//TODO why doesn't anything on the tempNode render when it's attached directly to the rootNode???
	Node tempNode = new Node(Constants.TEMP_NODE);
	DataStore.rootNode.attachChild(tempNode);
	DataStore.tempNode = tempNode;

	Node voxelWireframeNode = new Node(Constants.VOXEL_WIREFRAME_NODE);
	DataStore.rootNode.attachChild(voxelWireframeNode);
	DataStore.voxelWireframeNode = voxelWireframeNode;
    }

    protected void initPlayer() {
	flyCam.setDragToRotate(true);
	flyCam.setMoveSpeed(10f);
	cam.setLocation(new Vector3f(-7f, 20f, 13f));
	cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);

        Player player = new Player(cam);
        
        DataStore.player = player;
	DataStore.flyCam = flyCam;
    }

    protected void initGui() {
	NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager, inputManager, audioRenderer, guiViewPort);
	guiViewPort.addProcessor(niftyDisplay);
	DataStore.nifty = niftyDisplay.getNifty();
	
	//nifty logging is kindof intense...
	Logger.getLogger("de.lessvoid.nifty").setLevel(Level.SEVERE);
	Logger.getLogger("NiftyInputEventHandlingLog").setLevel(Level.SEVERE);
    }

    public static void createWorld() {
	DataStore.viewPort.setBackgroundColor(new ColorRGBA(0.4f, 0.4f, 1f, 1f));
        
	Main.createSun();
	
	Node chunk = WorldCreator.createChunk(new Vector3i(-5, -1, -5), new Vector3i(5, 1, 5));
        WorldManager.addChunkToWorld(chunk);
        
        AbstractCharacter char1 = CharacterCreator.createCharacter(VoxelGrid.getWorldLocation(new Vector3i(0, 3, 0)));
	char1.getInventory().addItem(new Axe());
        char1.getInventory().addItem(new Hammer());
        WorldManager.addCharacterToWorld(char1);

	AbstractCharacter char2 = CharacterCreator.createCharacter(VoxelGrid.getWorldLocation(new Vector3i(-3, 3, -2)));
	char2.getInventory().addItem(new Shovel());
        char2.getInventory().addItem(new Hammer());
        WorldManager.addCharacterToWorld(char2);
        
        Construct tree = Tree.createInstance(new Vector3i(3, 2, -3));
        WorldManager.addConstructToWorld(tree, DataStore.worldStateNode.getHubNodeSelectableConstruct());
    }

    public static void createSun() {
	DirectionalLight sun = new DirectionalLight();
	sun.setDirection(new Vector3f(-0.5f, -1f, -0.5f).normalize());
	sun.setColor(ColorRGBA.White);
	DataStore.worldStateNode.addLight(sun);
    }

    @Override
    public void simpleUpdate(float tpf) {
        
    }

    @Override
    public void simpleRender(RenderManager rm) {
	//TODO figure out the mathematics required to render only the voxels we can see. occlusion, hoooo!
    }
}
